// Copyright (C) 2022 Linlexiao
#include "pai.h"

#include <utility>


Pai::Pai(PaiType _type, int _num, bool _isRed) {
    type = _type;
    num = _num;
    isRed = _isRed;
    isAgari = false;
}

Pai::Pai(const QString &s)  // TODO 异常处理
{
    isRed = false;
    if (s[1].toLower() == 's') type = SUO;
    if (s[1].toLower() == 'm') type = MAN;
    if (s[1].toLower() == 'p') type = PIN;
    if (s[1].toLower() == 'z') type = ZI;
    num = s[0].digitValue();
    if (num == 0) {
        num = 5;
        isRed = true;
    }
    isAgari = false;
}

QString Pai::toString() {
    int x = num;
    if (isRed) x = 0; // 赤宝牌用0表示

    QString rt = QString::number(x);
    switch (type) {
        case SUO:
            rt += "s";
            break;
        case MAN:
            rt += "m";
            break;
        case PIN:
            rt += "p";
            break;
        case ZI:
            rt += "z";
            break;
    }
    return rt;
}

bool Pai::isYao() {
    if (type == PaiType::ZI) return true;
    if (num == 1 || num == 9) return true;
    return false;
}

bool Pai::isRyu() {
    if (type == ZI) {
        return num == 6;
    }
    if (type == SUO) {
        return num == 2 || num == 3 || num == 4 || num == 6 || num == 8;
    }
    return false;
}

int Pai::isYakuhai(HandFlags flag) {
    if (type != PaiType::ZI) {
        return 0;
    }
    int cnt = 0;
    switch (num) {
        case 1: // 东
            if (flag.testFlag(HandFlag::FIELD_EAST)) cnt++;
            if (flag.testFlag(HandFlag::SEAT_EAST)) cnt++;
            break;
        case 2: // 南
            if (flag.testFlag(HandFlag::FIELD_SOUTH)) cnt++;
            if (flag.testFlag(HandFlag::SEAT_SOUTH)) cnt++;
            break;
        case 3: // 西
            if (flag.testFlag(HandFlag::FIELD_WEST)) cnt++;
            if (flag.testFlag(HandFlag::SEAT_WEST)) cnt++;
            break;
        case 4: // 北
            if (flag.testFlag(HandFlag::FIELD_NORTH)) cnt++;
            if (flag.testFlag(HandFlag::SEAT_NORTH)) cnt++;
            break;
        case 5: // 白
            cnt++;
            break;
        case 6: // 发
            cnt++;
            break;
        case 7: // 中
            cnt++;
            break;
    }
    return cnt;
}

bool Pai::operator==(const Pai &rhs) const { // 是否相等（不比较赤宝牌）
    return type == rhs.type && num == rhs.num;
}

bool Pai::operator<(const Pai &rhs) const { // 比较函数（用于排序）
    return (type < rhs.type) || (type == rhs.type && num < rhs.num) ||
           (type == rhs.type && num == rhs.num && isAgari < rhs.isAgari);
}

Pai Pai::next() { // 获得下一张 （用于宝牌指示牌）
    Pai rt = *this;
    rt.num++;
    if (type != PaiType::ZI) {
        if (rt.num == 10) rt.num = 1;
    } else {
        if (rt.num == 5)
            rt.num = 1;
        else if (rt.num == 8)
            rt.num = 5;
    }
    return rt;
}

// 根据字符串返回牌的数组 e.g. "123s456p234m555z11s" 红宝牌用0表示
QVector<Pai> Pai::fromStrings(const QString &str) {
    QVector<Pai> rt;
    QVector<int> numbersGet;  // 目前暂时储存的数字

    for (auto ch : str) {
        if (ch.isDigit()) {
            numbersGet.push_back(ch.digitValue());
        } else {  //是牌类型
            if (ch.toLower() == 's') {
                while (numbersGet.length()) {
                    auto x = numbersGet.takeLast();
                    bool isRed = false;
                    if (x == 0) {
                        isRed = true;
                        x = 5;
                    }
                    rt.push_back(Pai(SUO, x, isRed));
                }
            } else if (ch.toLower() == 'p') {
                while (numbersGet.length()) {
                    auto x = numbersGet.takeLast();
                    bool isRed = false;
                    if (x == 0) {
                        isRed = true;
                        x = 5;
                    }
                    rt.push_back(Pai(PIN, x, isRed));
                }
            } else if (ch.toLower() == 'm') {
                while (numbersGet.length()) {
                    auto x = numbersGet.takeLast();
                    bool isRed = false;
                    if (x == 0) {
                        isRed = true;
                        x = 5;
                    }
                    rt.push_back(Pai(MAN, x, isRed));
                }
            } else if (ch.toLower() == 'z') {
                while (numbersGet.length()) {
                    auto x = numbersGet.takeLast();
                    bool isRed = false;
                    if (x == 0) {
                        isRed = true;
                        x = 5;
                    }
                    rt.push_back(Pai(ZI, x, isRed));
                }
            } else {
                // INVALID
                // TODO error message
            }
        }
    }

    return rt;
}

Block::Block(BlockType _blockType, PaiType _paiType, int _num, bool _isOpen,
             int _redCount) {
    blockType = _blockType;
    paiType = _paiType;
    num = _num;
    isOpen = _isOpen;
    redCount = _redCount;
}

bool Block::consistYao() {
    if (paiType == PaiType::ZI) {
        // 字牌一定是幺九面子
        return true;
    }
    if (blockType == BlockType::SEQUENCE) {
        // 123 789的顺子
        if (num == 1 || num == 7) return true;
    }
    if (blockType == BlockType::QUAD || blockType == BlockType::TRIPLET) {
        // 111 999 的刻子
        if (num == 1 || num == 9) return true;
    }
    return false;
}

int Block::consistYakuhai(HandFlags flag) {
    if (paiType != PaiType::ZI) {
        return 0;
    }
    int cnt = 0;
    switch (num) {
        case 1:
            if (flag.testFlag(HandFlag::FIELD_EAST)) cnt++;
            if (flag.testFlag(HandFlag::SEAT_EAST)) cnt++;
            break;
        case 2:
            if (flag.testFlag(HandFlag::FIELD_SOUTH)) cnt++;
            if (flag.testFlag(HandFlag::SEAT_SOUTH)) cnt++;
            break;
        case 3:
            if (flag.testFlag(HandFlag::FIELD_WEST)) cnt++;
            if (flag.testFlag(HandFlag::SEAT_WEST)) cnt++;
            break;
        case 4:
            if (flag.testFlag(HandFlag::FIELD_NORTH)) cnt++;
            if (flag.testFlag(HandFlag::SEAT_NORTH)) cnt++;
            break;
        case 5:
            cnt++;
            break;
        case 6:
            cnt++;
            break;
        case 7:
            cnt++;
            break;
    }
    return cnt;
}

bool Block::operator==(const Block &rhs) const {
    return paiType == rhs.paiType && blockType == rhs.blockType &&
           num == rhs.num;
}

Block Block::fromPais(QVector<Pai> pais, bool _isOpen) {
    std::sort(pais.begin(), pais.end());
    int redCnt = 0;
    for (auto p : pais) {
        redCnt += (int)p.isRed;
    }
    if (pais[0] == pais[1]) {
        //刻子或杠子
        return {pais.length() == 3 ? TRIPLET : QUAD, pais[0].type, pais[0].num,
                _isOpen, redCnt};
    }
    return {SEQUENCE, pais[0].type, pais[0].num, _isOpen, redCnt};
}

// 将Block转换成一个Pai的数组
QVector<Pai> Block::getPai() {
    int redRemain = redCount;
    QVector<Pai> rt;
    switch (blockType) {
        case SEQUENCE:
            for (int i = num; i < num + 3; i++) {
                bool isRed = false;
                if (i == 5 && redRemain > 0) {
                    redRemain--;
                    isRed = true;
                }
                rt.push_back(Pai(paiType, i, isRed));
            }
            break;
        case TRIPLET:
            for (int i = 0; i < 3; i++) {
                bool isRed = false;
                if (num == 5 && redRemain > 0 &&
                    i != 0) {  //第一个不能是红5 1.美观 2.防止暗杠挡住
                    redRemain--;
                    isRed = true;
                }
                rt.push_back(Pai(paiType, num, isRed));
            }
            break;
        case QUAD:
            for (int i = 0; i < 4; i++) {
                bool isRed = false;
                if (num == 5 && redRemain > 0 &&
                    i != 0) {  //第一个不能是红5 1.美观 2.防止暗杠挡住
                    redRemain--;
                    isRed = true;
                }
                rt.push_back(Pai(paiType, num, isRed));
            }
            break;
    }
    return rt;
}

HandSet::HandSet(QVector<Block> _blocks, Pair _pair, QVector<Pai> _dora,
                 QVector<Pai> _ura, MachiType _type, HandFlags _flag,
                 Pai _agariPai)
    : pair(_pair) {
    blocks = std::move(_blocks);
    dora = std::move(_dora);
    ura = std::move(_ura);
    type = _type;
    flag = _flag;
    agariPai = _agariPai;
}

Pair::Pair(PaiType _paiType, int _num, int _redCount) {
    paiType = _paiType;
    num = _num;
    redCount = _redCount;
}

QVector<Pai> Pair::getPai() {
    QVector<Pai> rt;
    int redRemain = redCount;
    for (int i = 0; i < 2; i++) {
        bool isRed = false;
        if (redRemain > 0) {
            isRed = true;
            redRemain--;
        }
        rt.push_back(Pai(paiType, num, isRed));
    }
    return rt;
}

bool Pair::consistYao() { return getPai()[0].isYao(); }

State::State(Wind field, Wind seat, const QVector<HandFlag> &yakus,
             HandFlag agariWay, QVector<Pai> p, QVector<Block> _furu,
             QVector<Pai> d, QVector<Pai> u, Pai _agariPai)
    : agariPai(_agariPai) {
    // 设置场风和自风
    if (field != EMPTY_WIND) flag.setFlag(HandFlag(1 << field));
    if (seat != EMPTY_WIND) flag.setFlag(HandFlag(1 << (seat + 4)));
    for (auto yaku : yakus) {
        flag.setFlag(yaku);
    }
    flag.setFlag(agariWay);

    furu = std::move(_furu);
    bool menzen = true;
    // 如果副露中有不是暗杠的副露，那么不是门清
    for (auto b : furu) {
        if (b.isOpen) {
            menzen = false;
            break;
        }
    }
    if (menzen) {
        flag.setFlag(MENZEN);
    }
    pais = std::move(p);
    dora = std::move(d);
    ura = std::move(u);
    agariPai.isAgari = true;
}
